VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "IValue"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Private Declare Function CreateSolidBrush Lib "gdi32.dll" (ByVal crColor As Long) As Long
Private Declare Function FillRect Lib "user32.dll" (ByVal hdc As Long, ByRef lpRect As RECT, ByVal hBrush As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type typeValue
 nType As Long
 nValue As Currency
 nDenominator As Currency
 fValue As Double
End Type

Private m_t As typeValue

Friend Sub CloneFrom(t As typeValue)
On Error Resume Next
m_t = t
End Sub

Friend Sub CloneTo(t As typeValue)
On Error Resume Next
t = m_t
End Sub

Friend Function Clone() As iValue
Dim obj As New iValue
obj.CloneFrom m_t
Set Clone = obj
End Function

Friend Property Get ValueType() As Long
ValueType = m_t.nType
End Property

Friend Function ToString() As String
On Error Resume Next
Dim s As String
Select Case m_t.nType
Case IValue_Integer
 ToString = CStr(m_t.nValue)
Case IValue_Rational
 ToString = CStr(m_t.nValue) + "/" + CStr(m_t.nDenominator)
Case IValue_Real
 s = CStr(m_t.fValue)
 If InStr(1, s, ".") = 0 And InStr(1, s, "e", vbTextCompare) = 0 Then
  s = s + ".0"
 ElseIf Left(s, 1) = "." Then
  s = "0" + s
 End If
 ToString = s
End Select
End Function

Friend Function CurrencyGCD(ByVal a As Currency, ByVal b As Currency) As Currency
On Error Resume Next
Dim c As Currency
If a < 0 Then a = -a
If b < 0 Then b = -b
Do Until b = 0
 c = CurrencyMod(a, b)
 a = b
 b = c
Loop
CurrencyGCD = a
End Function

'assume a>0 and b>0
Friend Function CurrencyDiv(ByVal a As Currency, ByVal b As Currency) As Currency
On Error Resume Next
CurrencyDiv = CCur(Int(CDbl(a) / CDbl(b))) '???
End Function

'assume a>0 and b>0
Friend Function CurrencyMod(ByVal a As Currency, ByVal b As Currency) As Currency
On Error Resume Next
a = a - CurrencyDiv(a, b) * b
Debug.Assert a >= 0 And a < b
CurrencyMod = a
End Function

Friend Sub NormalizeRational()
On Error Resume Next
Dim n As Currency
If m_t.nDenominator < 0 Then
 m_t.nValue = -m_t.nValue
 m_t.nDenominator = -m_t.nDenominator
End If
n = CurrencyGCD(m_t.nValue, m_t.nDenominator)
If n > 1 Then
 m_t.nValue = CurrencyDiv(m_t.nValue, n)
 m_t.nDenominator = CurrencyDiv(m_t.nDenominator, n)
End If
End Sub

Friend Sub FromString(ByVal s As String)
On Error Resume Next
Dim i As Long
s = Trim(s)
i = InStr(1, s, "/")
If i > 0 Then
 m_t.nType = IValue_Rational
 m_t.nValue = CCur(Left(s, i - 1))
 m_t.nDenominator = CCur(Mid(s, i + 1))
 NormalizeRational
ElseIf InStr(1, s, ".") > 0 Or InStr(1, s, "e", vbTextCompare) > 0 Then
 m_t.nType = IValue_Real
 m_t.fValue = Val(s)
ElseIf IsNumeric(s) Then
 m_t.nType = IValue_Integer
 m_t.nValue = CCur(s)
Else
 m_t.nType = IValue_Void
End If
End Sub

Friend Function ToDouble() As Double
On Error Resume Next
Select Case m_t.nType
Case IValue_Integer
 ToDouble = m_t.nValue
Case IValue_Rational
 ToDouble = m_t.nValue / m_t.nDenominator
Case IValue_Real
 ToDouble = m_t.fValue
End Select
End Function

Friend Sub FromDouble(ByVal n As Double)
On Error Resume Next
m_t.nType = IValue_Real
m_t.fValue = n
End Sub

Friend Sub FromCurrency(ByVal n As Currency, Optional ByVal nDenominator As Currency = 1)
On Error Resume Next
m_t.nType = IValue_Rational
m_t.nValue = n
m_t.nDenominator = nDenominator
If m_t.nDenominator <> 1 Then NormalizeRational
If m_t.nDenominator = 1 Then m_t.nType = IValue_Integer
End Sub

'force convert to specified type
Friend Function ConvertTo(ByVal nType As Long) As Boolean
On Error Resume Next
Select Case nType
Case IValue_Integer
 Select Case m_t.nType
 Case IValue_Integer
  ConvertTo = True
 Case IValue_Rational
  ConvertTo = m_t.nDenominator = 1
  If m_t.nDenominator = 0 Then
   m_t.nValue = 0
  Else
   m_t.nValue = Round(m_t.nValue / m_t.nDenominator)
  End If
 Case IValue_Real
  m_t.nValue = Round(m_t.fValue)
  ConvertTo = m_t.nValue = m_t.fValue
 Case Else
  m_t.nValue = 0
 End Select
 m_t.nType = IValue_Integer
Case IValue_Rational
 Select Case m_t.nType
 Case IValue_Integer
  m_t.nDenominator = 1
  ConvertTo = True
  Exit Function '??????
 Case IValue_Rational
  ConvertTo = True
  If m_t.nDenominator = 1 Then m_t.nType = IValue_Integer '??????
  Exit Function '??????
 Case IValue_Real
  'TODO: continued fraction
  m_t.nValue = Round(m_t.fValue)
  m_t.nDenominator = 1
  ConvertTo = m_t.nValue = m_t.fValue
 Case Else
  m_t.nValue = 0
 End Select
 m_t.nType = IValue_Rational
Case IValue_Real
 m_t.fValue = ToDouble
 m_t.nType = IValue_Real
 ConvertTo = True
Case Else
 m_t.nType = IValue_Void
 ConvertTo = True
End Select
End Function

'operator-
Friend Sub OperatorMinus()
On Error Resume Next
Select Case m_t.nType
Case IValue_Integer, IValue_Rational
 m_t.nValue = -m_t.nValue
Case IValue_Real
 m_t.fValue = -m_t.fValue
End Select
End Sub

Friend Sub OperatorAbs()
On Error Resume Next
Select Case m_t.nType
Case IValue_Integer, IValue_Rational
 If m_t.nValue < 0 Then m_t.nValue = -m_t.nValue
Case IValue_Real
 If m_t.fValue < 0 Then m_t.fValue = -m_t.fValue
End Select
End Sub

'operator+=
Friend Sub OperatorPlusEqual(ByVal obj As iValue)
On Error Resume Next
Dim t As typeValue
obj.CloneTo t
If m_t.nType = IValue_Void Or t.nType = IValue_Void Then
 m_t.nType = IValue_Void
ElseIf m_t.nType <= IValue_Integer And t.nType <= IValue_Integer Then
 m_t.nType = IValue_Integer
 m_t.nValue = m_t.nValue + t.nValue
ElseIf m_t.nType <= IValue_Rational And t.nType <= IValue_Rational Then
 If m_t.nType = IValue_Integer Then m_t.nDenominator = 1
 If t.nType = IValue_Integer Then t.nDenominator = 1
 m_t.nType = IValue_Rational
 m_t.nValue = m_t.nValue * t.nDenominator + t.nValue * m_t.nDenominator
 m_t.nDenominator = m_t.nDenominator * t.nDenominator
 NormalizeRational
 If m_t.nDenominator = 1 Then m_t.nType = IValue_Integer
Else
 m_t.fValue = ToDouble + obj.ToDouble
 m_t.nType = IValue_Real
End If
End Sub

'operator-=
Friend Sub OperatorMinusEqual(ByVal obj As iValue)
On Error Resume Next
Dim t As typeValue
obj.CloneTo t
If m_t.nType = IValue_Void Or t.nType = IValue_Void Then
 m_t.nType = IValue_Void
ElseIf m_t.nType <= IValue_Integer And t.nType <= IValue_Integer Then
 m_t.nType = IValue_Integer
 m_t.nValue = m_t.nValue - t.nValue
ElseIf m_t.nType <= IValue_Rational And t.nType <= IValue_Rational Then
 If m_t.nType = IValue_Integer Then m_t.nDenominator = 1
 If t.nType = IValue_Integer Then t.nDenominator = 1
 m_t.nType = IValue_Rational
 m_t.nValue = m_t.nValue * t.nDenominator - t.nValue * m_t.nDenominator
 m_t.nDenominator = m_t.nDenominator * t.nDenominator
 NormalizeRational
 If m_t.nDenominator = 1 Then m_t.nType = IValue_Integer
Else
 m_t.fValue = ToDouble - obj.ToDouble
 m_t.nType = IValue_Real
End If
End Sub

'operator*=
Friend Sub OperatorTimesEqual(ByVal obj As iValue)
On Error Resume Next
Dim t As typeValue
Dim n As Currency
obj.CloneTo t
If m_t.nType = IValue_Void Or t.nType = IValue_Void Then
 m_t.nType = IValue_Void
ElseIf m_t.nType <= IValue_Integer And t.nType <= IValue_Integer Then
 m_t.nType = IValue_Integer
 m_t.nValue = m_t.nValue * t.nValue
ElseIf m_t.nType <= IValue_Rational And t.nType <= IValue_Rational Then
 If m_t.nType = IValue_Integer Then m_t.nDenominator = 1
 If t.nType = IValue_Integer Then t.nDenominator = 1
 m_t.nType = IValue_Rational
 n = CurrencyGCD(m_t.nValue, t.nDenominator)
 If n > 1 Then
  m_t.nValue = CurrencyDiv(m_t.nValue, n)
  t.nDenominator = CurrencyDiv(t.nDenominator, n)
 End If
 n = CurrencyGCD(t.nValue, m_t.nDenominator)
 If n > 1 Then
  t.nValue = CurrencyDiv(t.nValue, n)
  m_t.nDenominator = CurrencyDiv(m_t.nDenominator, n)
 End If
 m_t.nValue = m_t.nValue * t.nValue
 m_t.nDenominator = m_t.nDenominator * t.nDenominator
 Debug.Assert m_t.nDenominator > 0
 Debug.Assert CurrencyGCD(m_t.nValue, m_t.nDenominator) = 1
 If m_t.nDenominator = 1 Then m_t.nType = IValue_Integer
Else
 m_t.fValue = ToDouble * obj.ToDouble
 m_t.nType = IValue_Real
End If
End Sub

'operator/=
Friend Sub OperatorDivideEqual(ByVal obj As iValue)
On Error Resume Next
Dim t As typeValue
Dim n As Currency
obj.CloneTo t
If m_t.nType = IValue_Void Or t.nType = IValue_Void Then
 m_t.nType = IValue_Void
ElseIf m_t.nType <= IValue_Rational And t.nType <= IValue_Rational Then
 If m_t.nType = IValue_Integer Then m_t.nDenominator = 1
 If t.nType = IValue_Integer Then t.nDenominator = 1
 m_t.nType = IValue_Rational
 n = CurrencyGCD(m_t.nValue, t.nValue)
 If n > 1 Then
  m_t.nValue = CurrencyDiv(m_t.nValue, n)
  t.nValue = CurrencyDiv(t.nValue, n)
 End If
 n = CurrencyGCD(t.nDenominator, m_t.nDenominator)
 If n > 1 Then
  t.nDenominator = CurrencyDiv(t.nDenominator, n)
  m_t.nDenominator = CurrencyDiv(m_t.nDenominator, n)
 End If
 m_t.nValue = m_t.nValue * t.nDenominator
 m_t.nDenominator = m_t.nDenominator * t.nValue
 If m_t.nDenominator < 0 Then
  m_t.nValue = -m_t.nValue
  m_t.nDenominator = -m_t.nDenominator
 End If
 Debug.Assert m_t.nDenominator > 0
 Debug.Assert CurrencyGCD(m_t.nValue, m_t.nDenominator) = 1
 If m_t.nDenominator = 1 Then m_t.nType = IValue_Integer
Else
 m_t.fValue = ToDouble / obj.ToDouble
 m_t.nType = IValue_Real
End If
End Sub

'operator%=
Friend Sub OperatorModEqual(ByVal obj As iValue)
On Error Resume Next
Dim n As Currency
If m_t.nType = IValue_Void Or obj.ValueType = IValue_Void Then
 m_t.nType = IValue_Void
Else
 'FIXME: calculate mod of rational?
 ConvertTo IValue_Integer
 n = Round(obj.ToDouble)
 m_t.nType = IValue_Integer
 m_t.nValue = CurrencyMod(m_t.nValue, n)
End If
End Sub

'return value (bitfield):
'&H1&=less
'&H2&=equal
'&H4&=greater
Friend Function Compare(ByVal obj As iValue) As Long
On Error Resume Next
Dim t As typeValue
Dim f As Double
Dim n As Currency
obj.CloneTo t
If m_t.nType = IValue_Void Or t.nType = IValue_Void Then
 Compare = &H7&
ElseIf m_t.nType <= IValue_Integer And t.nType <= IValue_Integer Then
 If m_t.nValue < t.nValue Then
  Compare = &H1&
 ElseIf m_t.nValue > t.nValue Then
  Compare = &H4&
 Else
  Compare = &H2&
 End If
ElseIf m_t.nType <= IValue_Rational And t.nType <= IValue_Rational Then
 If m_t.nType = IValue_Integer Then m_t.nDenominator = 1
 If t.nType = IValue_Integer Then t.nDenominator = 1
 If m_t.nDenominator = t.nDenominator Then
  If m_t.nValue < t.nValue Then
   Compare = &H1&
  ElseIf m_t.nValue > t.nValue Then
   Compare = &H4&
  Else
   Compare = &H2&
  End If
 Else
  f = m_t.nValue / m_t.nDenominator - t.nValue / t.nDenominator
  If f < -0.001 Then
   Compare = &H1&
  ElseIf f > 0.001 Then
   Compare = &H4&
  Else
   n = m_t.nValue * t.nDenominator - t.nValue * m_t.nDenominator
   If n > 0 Then
    Compare = &H1&
   ElseIf n < 0 Then
    Compare = &H4&
   Else
    Compare = &H2&
   End If
  End If
 End If
Else
 f = ToDouble - obj.ToDouble
 If f < -0.001 Then
  Compare = &H1&
 ElseIf f > 0.001 Then
  Compare = &H4&
 Else
  Compare = &H2&
 End If
End If
End Function

Friend Sub Draw(ByVal bm As cAlphaDibSection, ByVal nLeft As Long, ByVal nTop As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal nColor As Long, Optional ByVal sFormat As String)
On Error Resume Next
'///
Dim r As RECT
Dim hbr As Long
Dim i As Long
Dim obj1 As iValue, obj2 As iValue, obj3 As iValue
Dim s As String, s2 As String
Dim bDefault As Boolean 'draw without format
'///
If sFormat = vbNullString Then
 bDefault = True
Else
 Select Case m_t.nType
 Case IValue_Integer, IValue_Rational, IValue_Real
  If sFormat = "String" Then
   sFormat = ToString
  ElseIf Left(sFormat, 4) = "Pow(" Then
   sFormat = Mid(sFormat, 5)
   i = InStr(1, sFormat, ")")
   If i > 0 Then sFormat = Left(sFormat, i - 1)
   '///
   Set obj1 = New iValue 'the base
   Set obj2 = New iValue 'if the exponent >= this value then draw it as power
   i = InStr(1, sFormat, ",")
   If i > 0 Then
    obj1.FromString Trim(Left(sFormat, i - 1))
    obj2.FromString Trim(Mid(sFormat, i + 1))
   Else
    obj1.FromString Trim(sFormat)
   End If
   '///
   bDefault = True
   Set obj3 = obj1.Clone
   If obj3.OperatorLogEqual(Me) Then 'get the exponent
    If obj3.ConvertTo(IValue_Rational) Then
     If obj3.Compare(obj2) And &H6& Then
      '///base
      s = obj1.ToString
      If InStr(1, s, "+") > 0 Or InStr(1, s, "-") > 0 Or InStr(1, s, "/") > 0 Then
       s = "(" + s + ")"
      End If
      sFormat = s
      '///exponent
      s = obj3.ToString
      s2 = ToSuperscript(s)
      If Len(s) = Len(s2) Then
       sFormat = sFormat + s2
      Else
       If InStr(1, s, "+") > 0 Or InStr(1, s, "-") > 0 Or InStr(1, s, "/") > 0 Then
        s = "(" + s + ")"
       End If
       sFormat = sFormat + "^" + s
      End If
      bDefault = False
     End If
    End If
   End If
  ElseIf Left(sFormat, 9) = "NthPower(" Then
   sFormat = Mid(sFormat, 10)
   i = InStr(1, sFormat, ")")
   If i > 0 Then sFormat = Left(sFormat, i - 1)
   '///
   Set obj1 = New iValue 'the exponent
   Set obj2 = New iValue '1/exponent
   obj1.FromString Trim(sFormat)
   obj2.FromCurrency 1
   obj2.OperatorDivideEqual obj1
   '///
   bDefault = True
   Set obj3 = Me.Clone
   If obj3.OperatorExpEqual(obj2) Then 'get the base
    If obj3.ConvertTo(IValue_Rational) Then
     '///base
     s = obj3.ToString
     If InStr(1, s, "+") > 0 Or InStr(1, s, "-") > 0 Or InStr(1, s, "/") > 0 Then
      s = "(" + s + ")"
     End If
     sFormat = s
     '///exponent
     s = obj1.ToString
     s2 = ToSuperscript(s)
     If Len(s) = Len(s2) Then
      sFormat = sFormat + s2
     Else
      If InStr(1, s, "+") > 0 Or InStr(1, s, "-") > 0 Or InStr(1, s, "/") > 0 Then
       s = "(" + s + ")"
      End If
      sFormat = sFormat + "^" + s
     End If
     bDefault = False
    End If
   End If
  Else
   sFormat = Format(ToDouble, sFormat)
  End If
 End Select
 If Not bDefault Then DrawTextAccordingToSize bm.hdc, sFormat, nLeft, nTop, nWidth, nHeight, nColor
End If
'///
If bDefault Then
 Select Case m_t.nType
 Case IValue_Integer
  DrawTextAccordingToSize bm.hdc, CStr(m_t.nValue), nLeft, nTop, nWidth, nHeight, nColor
 Case IValue_Rational
  'TODO: draw it nicer
  r.Left = nLeft + 8
  r.Right = nLeft + nWidth - 8
  r.Top = nTop + nHeight \ 2 - 1
  r.Bottom = r.Top + 2
  hbr = CreateSolidBrush(nColor)
  FillRect bm.hdc, r, hbr
  DeleteObject hbr
  DrawTextAccordingToSize bm.hdc, CStr(m_t.nValue), nLeft, nTop, nWidth, r.Top - nTop, nColor
  DrawTextAccordingToSize bm.hdc, CStr(m_t.nDenominator), nLeft, r.Bottom, nWidth, nTop + nHeight - r.Bottom, nColor
 Case IValue_Real
  DrawTextAccordingToSize bm.hdc, CStr(m_t.fValue), nLeft, nTop, nWidth, nHeight, nColor
 End Select
End If
End Sub

'operator^=
'NOTE: if return value is False then the internal state is undefined
Friend Function OperatorExpEqual(ByVal obj As iValue) As Boolean
On Error Resume Next
Dim t As typeValue
Dim e1 As Long, e2 As Long 'FIXME: currency support
Dim f As Double
Dim tmp As Currency
Dim bInvert As Boolean
'///
obj.CloneTo t
'///
Err.Clear
If m_t.nType = IValue_Void Or t.nType = IValue_Void Then
 m_t.nType = IValue_Void
ElseIf t.nType <= IValue_Rational Then
 e1 = t.nValue
 If t.nType = IValue_Integer Then e2 = 1 Else e2 = t.nDenominator
 If e2 = 0 Then Exit Function
 If e1 < 0 Then
  e1 = -e1
  bInvert = True
 End If
 '///
 Do While m_t.nType <= IValue_Rational 'rational^rational
  If m_t.nType = IValue_Integer Then m_t.nDenominator = 1
  m_t.nType = IValue_Rational
  If bInvert Then
   If m_t.nValue < 0 Then
    t.nValue = -m_t.nDenominator
    t.nDenominator = -m_t.nValue
   Else
    t.nValue = m_t.nDenominator
    t.nDenominator = m_t.nValue
   End If
  Else
   t.nValue = m_t.nValue
   t.nDenominator = m_t.nDenominator
  End If
  Debug.Assert t.nDenominator > 0
  '///calculate n-th roots first
  If e2 > 1 Then
   If t.nValue < 0 Then
    If e2 And 1& Then
     f = Round((-t.nValue) ^ (1 / e2))
     tmp = f ^ e2
     If tmp <> -t.nValue Then 'the root is non-rational
      m_t.fValue = m_t.nValue / m_t.nDenominator
      m_t.nType = IValue_Real
      Exit Do
     End If
     t.nValue = -f
    Else
     Exit Function 'negative number doesn't have square root
    End If
   Else
    f = Round(t.nValue ^ (1 / e2))
    tmp = f ^ e2
    If tmp <> t.nValue Then 'the root is non-rational
     m_t.fValue = m_t.nValue / m_t.nDenominator
     m_t.nType = IValue_Real
     Exit Do
    End If
    t.nValue = f
   End If
   f = Round(t.nDenominator ^ (1 / e2))
   tmp = f ^ e2
   If tmp <> t.nDenominator Then 'the root is non-rational
    m_t.fValue = m_t.nValue / m_t.nDenominator
    m_t.nType = IValue_Real
    Exit Do
   End If
   t.nDenominator = f
  End If
  '///calculate power
  'starting from here the value of m_t is screwed up
  m_t.nValue = 1
  m_t.nDenominator = 1
  Do
   If e1 And 1& Then
    m_t.nValue = m_t.nValue * t.nValue
    m_t.nDenominator = m_t.nDenominator * t.nDenominator
   End If
   e1 = e1 \ 2
   If e1 = 0 Then Exit Do
   t.nValue = t.nValue * t.nValue
   t.nDenominator = t.nDenominator * t.nDenominator
  Loop
  '///over
  Debug.Assert m_t.nDenominator > 0
  If m_t.nDenominator = 1 Then m_t.nType = IValue_Integer
  Exit Do
 Loop
 If m_t.nType = IValue_Real Then 'real^rational
  If bInvert Then f = 1 / m_t.fValue Else f = m_t.fValue
  '///calculate n-th roots first
  If e2 > 1 Then
   If f < 0 Then
    If e2 And 1& Then
     f = -((-f) ^ (1 / e2))
    Else
     Exit Function 'negative number doesn't have square root
    End If
   Else
    f = f ^ (1 / e2)
   End If
  End If
  '///calculate power
  'starting from here the value of m_t is screwed up
  m_t.fValue = 1
  Do
   If e1 And 1& Then
    m_t.fValue = m_t.fValue * f
   End If
   e1 = e1 \ 2
   If e1 = 0 Then Exit Do
   f = f * f
  Loop
 End If
Else 'real^real
 m_t.fValue = ToDouble ^ obj.ToDouble
 m_t.nType = IValue_Real
End If
If Err.Number Then Exit Function
OperatorExpEqual = True
End Function

'NOTE: if return value is False then the internal state is undefined
Friend Function OperatorLogEqual(ByVal obj As iValue) As Boolean
On Error Resume Next
Dim t As typeValue
Dim tmp0 As Currency, tmp As Currency
Dim tmp1 As Currency, tmp2 As Currency
Dim n As Long
Dim bInvert As Boolean
Dim bReal As Boolean
'///
obj.CloneTo t
'///
Err.Clear
If m_t.nType = IValue_Void Or t.nType = IValue_Void Then
 m_t.nType = IValue_Void
Else
 bReal = True
 Do While m_t.nType <= IValue_Rational And t.nType <= IValue_Rational 'log_rational(rational)
  bReal = False
  If m_t.nType = IValue_Integer Then m_t.nDenominator = 1
  If t.nType = IValue_Integer Then t.nDenominator = 1
  m_t.nType = IValue_Rational
  '///
  If t.nValue <= 0 Or m_t.nValue <= 0 Then Exit Function 'only positive number allowed
  If m_t.nValue = 1 And m_t.nDenominator = 1 Then Exit Function 'no log_1(x)
  If t.nValue = 1 And t.nDenominator = 1 Then 'log_x(1)
   m_t.nValue = 0
   m_t.nType = IValue_Integer
   Exit Do
  End If
  '///convert such that both are >1
  If m_t.nValue < m_t.nDenominator Then
   tmp = m_t.nValue
   m_t.nValue = m_t.nDenominator
   m_t.nDenominator = tmp
   bInvert = Not bInvert
  End If
  If t.nValue < t.nDenominator Then
   tmp = t.nValue
   t.nValue = t.nDenominator
   t.nDenominator = tmp
   bInvert = Not bInvert
  End If
  Debug.Assert m_t.nValue > 1 And t.nValue > 1
  '///calculate log_numerator(numerator)
  If m_t.nValue > t.nValue Then tmp0 = t.nValue Else tmp0 = m_t.nValue 'get the smallest numerator
  For n = 1 To 100 'try n-th root of it
   tmp = Round(tmp0 ^ (1 / n))
   If tmp <= 1 Then
    bReal = True
    Exit Do
   End If
   tmp1 = Round(Log(m_t.nValue) / Log(tmp))
   tmp2 = Round(Log(t.nValue) / Log(tmp))
   If CCur(Round(tmp ^ tmp1)) = m_t.nValue And CCur(Round(tmp ^ tmp2)) = t.nValue Then Exit For
  Next n
  tmp = CurrencyGCD(tmp1, tmp2)
  Debug.Assert tmp >= 1
  tmp1 = tmp1 / tmp
  tmp2 = tmp2 / tmp
  '///check denominator
  tmp = Round(m_t.nDenominator ^ (1 / tmp1))
  If CCur(Round(tmp ^ tmp1)) <> m_t.nDenominator Or CCur(Round(tmp ^ tmp2)) <> t.nDenominator Then
   bReal = True
   Exit Do
  End If
  '///now we confirm that the result is a rational number
  If bInvert Then m_t.nValue = -tmp2 Else m_t.nValue = tmp2
  m_t.nDenominator = tmp1
  If tmp1 = 1 Then m_t.nType = IValue_Integer
  Exit Do
 Loop
 If bReal Then 'log_real(real)
  If bInvert Then
   Debug.Assert m_t.nType = IValue_Rational
   tmp = m_t.nValue
   m_t.nValue = m_t.nDenominator
   m_t.nDenominator = tmp
  End If
  m_t.fValue = Log(obj.ToDouble) / Log(ToDouble)
  m_t.nType = IValue_Real
 End If
End If
If Err.Number Then Exit Function
OperatorLogEqual = True
End Function

